using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Xna.Framework;

namespace GameFramework.Extensions
{
    /// <summary>
    /// Binary Reader Extension Methods.
    /// </summary>
    [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed. Suppression is OK here.")]
    public static class BinaryReaderExtensions
    {
        #region Value Types

        public static int ReadInt(this BinaryReader reader)
        {
            return reader.ReadInt32();
        }

        public static float ReadFloat(this BinaryReader reader)
        {
            return reader.ReadSingle();
        }

        public static DateTime ReadDateTime(this BinaryReader reader)
        {
            var int64 = reader.ReadInt64();
            return new DateTime(int64);
        }

        public static TimeSpan ReadTimeSpan(this BinaryReader reader)
        {
            var int64 = reader.ReadInt64();
            return new TimeSpan(int64);
        }

        public static Rectangle ReadRectangle(this BinaryReader reader)
        {
            return new Rectangle(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32());
        }

        public static Color ReadColor(this BinaryReader reader)
        {
            return new Color(reader.ReadVector4());
        }

        public static Vector2 ReadVector2(this BinaryReader reader)
        {
            return new Vector2(reader.ReadSingle(), reader.ReadSingle());
        }

        public static Vector3 ReadVector3(this BinaryReader reader)
        {
            return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
        }

        public static Vector4 ReadVector4(this BinaryReader reader)
        {
            return new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
        }

        #endregion

        #region Reference Types

        //// The BCL already implement ReadString.

        public static T ReadObject<T>(this BinaryReader reader) where T : IBinarySerializable, new()
        {
            if (reader.ReadBoolean())
            {
                T result = new T();
                result.Read(reader);
                return result;
            }
            return default(T);
        }

        #endregion

        #region List of Reference Types (Exception Strings)

        public static List<T> ReadList<T>(this BinaryReader reader) where T : IBinarySerializable, new()
        {
            int count = reader.ReadInt32();

            var list = new List<T>();

            for (int i = 0; i < count; i++)
            {
                T item = new T();
                item.Read(reader);
                list.Add(item);
            }

            return list;
        }

        #endregion

        #region List of Strings and Value Types

        public static List<string> ReadStringList(this BinaryReader reader)
        {
            int count = reader.ReadInt32();

            var list = new List<string>();
             
            for (int i = 0; i < count; i++)
            {
                list.Add(reader.ReadString());
            }
                
            return list;
        }

        #endregion

        #region Array of Reference Types

        public static T[] ReadArray<T>(this BinaryReader reader) where T : IBinarySerializable, new()
        {
            int count = reader.ReadInt32();

            var array = new T[count];

            for (int i = 0; i < count; i++)
            {
                array[i] = new T();
                array[i].Read(reader);
            }

            return array;
        }

        #endregion

        #region Array of Value Types

        public static float[] ReadFloatArray(this BinaryReader reader)
        {
            int count = reader.ReadInt();

            var array = new float[count];

            for (int i = 0; i < count; i++)
            {
                array[i] = reader.ReadSingle();
            }

            return array;
        }

        public static Vector2[] ReadVector2Array(this BinaryReader reader)
        {
            int count = reader.ReadInt32();

            var array = new Vector2[count];

            for (int i = 0; i < count; i++)
            {
                array[i] = reader.ReadVector2();
            }

            return array;
        }

        #endregion
    }
}